/*
 * Copyright (c) 2022-2023 cetcclout Co., Ltd All rights reserved.
 * Sensor Adapter is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package com.cetccloud.cimp.common.util;

import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cglib.beans.BeanMap;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author
 * @apiNote 树状结构工具类
 * @date 2022-09-23
 */
public class TreeUtils {

    private TreeUtils() {
    }

    /**
     * 默认的配置对象
     */
    private static final TreeNodeConfig DEFAULT_CONFIG = createConfig(null, null, "sort"
            , null, null);


    /**
     * 将源数据转化为树结构数据
     */
    public static <T, E> List<Tree<E>> transform(List<T> treeDemoList, E rootId, TreeNodeConfig config) {
        if (CollectionUtils.isNotEmpty(treeDemoList)) {
            //#转树，Tree<>里面泛型为id的类型
            return TreeUtil.build(treeDemoList, rootId, config, (object, tree) -> {
                Map<String, T> map = BeanMap.create(object);
                for (Map.Entry<String, T> entry : map.entrySet()) {
                    tree.putExtra(entry.getKey(), entry.getValue());
                }
            });
        }
        return Lists.newArrayList();
    }

    /**
     * 将源数据转化为树结构数据
     *
     * @param treeDemoList 数据集
     * @param rootId       根节点id
     */
    public static <T, E> List<Tree<E>> transform(List<T> treeDemoList, E rootId) {
        return transform(treeDemoList, rootId, DEFAULT_CONFIG);
    }

    /**
     * 子节点查找父节点集(包含节点本身)
     */
    public static <T, K> List<T> childToParent(List<T> treeDemoList, T node, Function<? super T, ? extends K> idMapper
            , Function<? super T, ? extends K> parentIdMapper) {
        if (CollectionUtils.isNotEmpty(treeDemoList)) {
            //通过set添加查找后的实体
            final Set<T> nodeList = new HashSet<>();
            //将源数据转化为map
            Map<? extends K, T> allMap = treeDemoList.stream().collect(
                    Collectors.toMap(idMapper, Function.identity(), (v1, v2) -> v2)
            );
            findParent(nodeList, node, allMap, parentIdMapper);
            return new ArrayList<>(nodeList);
        }
        return Lists.newArrayList();
    }

    /**
     * 节点查找子节点
     */
    public static <T, E> Tree<E> parentToChile(List<T> treeDemoList, E id, E rootId) {
        return parentToChile(transform(treeDemoList, rootId), id);
    }

    /**
     * 节点查找子节点
     */
    public static <E> Tree<E> parentToChile(List<Tree<E>> build, E id) {
        Tree<E> childNode = new Tree<>();
        if (CollectionUtils.isNotEmpty(build)) {
            for (Tree<E> node : build) {
                childNode = TreeUtil.getNode(node, id);
                if (Objects.nonNull(childNode)) {
                    break;
                }
            }
        }
        return childNode;
    }

    /**
     * 递归查找父节点
     */
    private static <T, K> void findParent(Set<T> nodeList, T current, Map<? extends K, T> allMap
            , Function<? super T, ? extends K> parentIdMapper) {
        if (Objects.nonNull(current)) {
            nodeList.add(current);
        }
        if (Objects.nonNull(current)) {
            findParent(nodeList, allMap.get(parentIdMapper.apply(current)), allMap, parentIdMapper);
        }
    }

    /**
     * 默认的树状构建配置
     *
     * @param idKey
     * @param parentIdKey
     * @param weighKey
     * @param deep        解析的最大深度
     * @return
     */
    private static TreeNodeConfig createConfig(String idKey, String parentIdKey, String weighKey
            , String childrenKey, Integer deep) {
        //#配置
        TreeNodeConfig config = new TreeNodeConfig();
        if (StringUtils.isNotBlank(idKey)) {
            config.setIdKey(idKey);
        }
        if (StringUtils.isNotBlank(parentIdKey)) {
            config.setParentIdKey(parentIdKey);
        }
        if (StringUtils.isNotBlank(weighKey)) {
            config.setWeightKey(weighKey);
        }
        if (StringUtils.isNotBlank(childrenKey)) {
            config.setChildrenKey(childrenKey);
        }
        if (deep != null) {
            config.setDeep(deep);
        }
        return config;
    }
}
